import org.jfree.ui.tabbedui.TabbedApplet;

public class SignalOperations {

	private Signal result;
	private byte[] array;
	private double f;
	private int samples;
	private short t0;
	private double[] content;

	public void computeSamples(Signal s1, Signal s2) {
		f = s1.getF();
		if (s1.getT0() != s2.getT0()) {

			double tks1 = s1.getT0() + (s1.getSamples() / s1.getF());
			double tks2 = s2.getT0() + (s2.getSamples() / s2.getF());
			double length = Math.abs(Math.max(tks2, tks1)
					- Math.min(s1.getT0(), s2.getT0()));
			samples = (int) (length * f);
			t0 = (short) Math.min(s1.getT0(), s2.getT0());
			System.out.println("SAMPLES: " + samples);
		} else {
			samples = s1.getSamples();
			t0 = s1.getT0();
		}
	}

	public Signal addSignals(Signal s1, Signal s2) {
		computeSamples(s1, s2);
		double probka = 0.0;
		content = new double[samples];
		int p1 = 0;
		int p2 = 0;
		double tmp;
		int tmp2;
		for (int i = 0; i < samples; i++) {
			content[i] = 0;
			probka = t0 + i / f;
			if (probka > s1.getT0()
					&& probka < s1.getT0() + (s1.getSamples() / s1.getF())) {
				tmp = 0;
				tmp2 = 0;
				tmp = Math.round((probka - s1.getT0()) * s1.getF());
				tmp = tmp / s1.getF();
				tmp = tmp * s1.getF();
				tmp2 = (int) tmp;
				p1 = (int) ((probka - s1.getT0()) * s1.getF());
				if (tmp2 < s1.getSamples() - 1) {
					content[i] += s1.getContent()[tmp2];
				}
			}
			if (probka > s2.getT0()
					&& probka < s2.getT0() + (s2.getSamples() / s2.getF())) {
				tmp = 0;
				tmp2 = 0;
				tmp = Math.round((probka - s2.getT0()) * s2.getF());
				tmp = tmp / s2.getF();
				tmp = tmp * s2.getF();
				tmp2 = (int) tmp;
				p2 = (int) ((probka - s2.getT0()) * s2.getF());
				if (tmp2 < s2.getSamples() - 1) {
					content[i] += s2.getContent()[tmp2];
				}
			}
		}
		result = new Signal(t0, f, samples, content);
		return result;
	}

	public Signal substSignals(Signal s1, Signal s2) {
		computeSamples(s1, s2);
		double probka = 0.0;
		content = new double[samples];
		int p1 = 0;
		int p2 = 0;
		double tmp;
		int tmp2;
		for (int i = 0; i < samples; i++) {
			content[i] = 0;
			p1 = 0;
			p2 = 0;
			probka = t0 + i / f;
			if (probka > s1.getT0()
					&& probka < s1.getT0() + (s1.getSamples() / s1.getF())) {
				tmp = 0;
				tmp2 = 0;
				tmp = Math.round((probka - s1.getT0()) * s1.getF());
				tmp = tmp / s1.getF();
				tmp = tmp * s1.getF();
				tmp2 = (int) tmp;
				p1 = tmp2;
			}

			if (probka > s2.getT0()
					&& probka < s2.getT0() + (s2.getSamples() / s2.getF())) {
				tmp = 0;
				tmp2 = 0;
				tmp = Math.round((probka - s2.getT0()) * s2.getF());
				tmp = tmp / s2.getF();
				tmp = tmp * s2.getF();
				tmp2 = (int) tmp;
				p2 = tmp2;
			}
			if (s1.getContent()[p1] >= s2.getContent()[p2]) {
				content[i] = s1.getContent()[p1]
						- Math.abs(s2.getContent()[p2]);
				// System.out.println(s1.getContent()[p1]+"-"+s2.getContent()[p2]+"="+content[i]);
			}
			if (s2.getContent()[p2] > s1.getContent()[p1]) {
				content[i] = s2.getContent()[p2]
						- Math.abs(s1.getContent()[p1]);
				// System.out.println(s1.getContent()[p1]+"-"+s2.getContent()[p2]+"="+content[i]);
			} else {
				content[i] = 0.0;
			}

		}
		result = new Signal(t0, f, samples, content);
		return result;
	}

	public Signal multSignals(Signal s1, Signal s2) {
		computeSamples(s1, s2);
		double probka = 0.0;
		content = new double[samples];
		boolean f1;
		boolean f2;
		int p1 = 0;
		int p2 = 0;
		double tmp;
		int tmp2;
		for (int i = 0; i < samples; i++) {
			f1 = false;
			f2 = false;
			p1 = 0;
			p2 = 0;
			content[i] = 0;
			probka = t0 + i / f;
			if (probka > s1.getT0()
					&& probka < s1.getT0() + (s1.getSamples() / s1.getF())) {
				tmp = 0;
				tmp2 = 0;
				tmp = Math.round((probka - s1.getT0()) * s1.getF());
				tmp = tmp / s1.getF();
				tmp = tmp * s1.getF();
				tmp2 = (int) tmp;
				if (tmp2 < s1.getSamples() - 1) {
					f1 = true;
					p1 = tmp2;
				}
			}
			if (probka > s2.getT0()
					&& probka < s2.getT0() + (s2.getSamples() / s2.getF())) {
				tmp = 0;
				tmp2 = 0;
				tmp = Math.round((probka - s2.getT0()) * s2.getF());
				tmp = tmp / s2.getF();
				tmp = tmp * s2.getF();
				tmp2 = (int) tmp;
				if (tmp2 < s2.getSamples() - 1) {
					f2 = true;
					p2 = tmp2;
				}
			}
			if (f1 && f2 == true) {
				content[i] = s1.getContent()[p1] * s2.getContent()[p2];
			} else
				content[i] = 0;
		}
		result = new Signal(t0, f, samples, content);
		return result;
	}

	public Signal divideSignals(Signal s1, Signal s2) {
		computeSamples(s1, s2);
		double probka = 0.0;
		content = new double[samples];
		boolean f1;
		boolean f2;
		int p1 = 0;
		int p2 = 0;
		double tmp;
		int tmp2;
		for (int i = 0; i < samples; i++) {
			f1 = false;
			f2 = false;
			p1 = 0;
			p2 = 0;
			content[i] = 0;
			probka = t0 + i / f;
			if (probka > s1.getT0()
					&& probka < s1.getT0() + (s1.getSamples() / s1.getF())) {
				tmp = 0;
				tmp2 = 0;
				tmp = Math.round((probka - s1.getT0()) * s1.getF());
				tmp = tmp / s1.getF();
				tmp = tmp * s1.getF();
				tmp2 = (int) tmp;
				if (tmp2 < s1.getSamples() - 1) {
					f1 = true;
					p1 = tmp2;
				}
			}
			if (probka > s2.getT0()
					&& probka < s2.getT0() + (s2.getSamples() / s2.getF())) {
				tmp = 0;
				tmp2 = 0;
				tmp = Math.round((probka - s2.getT0()) * s2.getF());
				tmp = tmp / s2.getF();
				tmp = tmp * s2.getF();
				tmp2 = (int) tmp;
				if (tmp2 < s2.getSamples() - 1) {
					f2 = true;
					p2 = tmp2;
				}
			}
			if (f1 && f2 == true) {
				content[i] = s1.getContent()[p1] / s2.getContent()[p2];
			} else
				content[i] = 0;

		}
		result = new Signal(t0, f, samples, content);
		return result;
	}

	public Signal splot(Signal s1, Signal s2) {
		double[] tab = new double[s1.getSamples() + s2.getSamples() - 1];
		for (int n = 0; n < tab.length - 1; n++) {
			for (int k = 0; k <= s1.getSamples() - 1; k++) {
				if (k > s1.getSamples() - 1 || k < 0
						|| n - k > s2.getSamples() - 1 || n - k < 0) {
					tab[n] += 0;
				} else {
					tab[n] += s1.getContent()[k] * s2.getContent()[n - k];
					System.out.println("k: " + k + " | n-k: " + (n - k));
				}
			}
		}
		result = new Signal(s1.getT0(), s1.getF() * 2, tab.length, tab);
		return result;
	}

	public Signal korelacja(Signal s1, Signal s2) {
		int roznica = Math.max(s1.getSamples(), s2.getSamples())
				- Math.min(s1.getSamples(), s2.getSamples()) + 1;
		roznica -= Math.min(s1.getSamples(), s2.getSamples());
		double[] tab = new double[s1.getSamples() + s2.getSamples() - 1];
		int z = 0;
		for (int n = roznica; n < tab.length - Math.abs(roznica) - 1; n++) {
			for (int k = 0; k <= s1.getSamples() - 1; k++) {
				if (k > s1.getSamples() - 1 || k < 0
						|| n + k > s2.getSamples() - 1 || n + k < 0) {
					tab[z] += 0;
				} else {
					tab[z] += s1.getContent()[k] * s2.getContent()[n + k];
					// System.out.println("k: " + k + " | n+k: " + (n - k));
				}
			}
			z++;
		}
		result = new Signal(s1.getT0(), s1.getF() * 2, tab.length, tab);
		return result;
	}

	public Signal korelacja2(Signal s1, Signal s2) {
		double[] tab = new double[s1.getSamples() + s2.getSamples() - 1];
		double[] temp = new double[s2.getSamples()];
		// reverse tab
		int c = 0;
		for (int j = (s2.getSamples() - 1); j >= 0; j--) {
			temp[c] = s2.getContent()[j];
			c++;
		}
		for (int n = 0; n < tab.length - 1; n++) {
			for (int k = 0; k <= s1.getSamples() - 1; k++) {
				if (k > s1.getSamples() - 1 || k < 0
						|| n - k > s2.getSamples() - 1 || n - k < 0) {
					tab[n] += 0;
				} else {
					tab[n] += s1.getContent()[k] * temp[n - k];
					System.out.println("k: " + k + " | n-k: " + (n - k));
				}
			}
		}

		result = new Signal(s1.getT0(), s1.getF() * 2, tab.length, tab);
		return result;
	}

	public double h(int n, int m, int K) {
		double h = 0;
		int nrOkna = 1;
		int nrFiltru = 1;
		double l = 0;
		if (n == (m - 1) / 2)
			h = 2.0 / K;
		else
			h = (Math.sin((2 * Math.PI * (n - ((m - 1) / 2.0))) / K) / (Math.PI * (n - ((m - 1) / 2.0))));
		switch (nrOkna) {
		case 1:
			h = h * (0.53836 - 0.46164 * Math.cos((2 * Math.PI * n) / m));
			break;
		case 2:
			h = h * (0.5 - 0.5 * Math.cos((2 * Math.PI * n) / m));
			break;
		case 3:
			h = h
					* ((0.42 - 0.5 * Math.cos((2 * Math.PI * n) / m)) + (0.08 * Math
							.cos((4 * Math.PI * n) / m)));
			break;
		}
		switch (nrFiltru) {
		case 1:
			h = h * Math.sin((Math.PI * n) / 2);
			break;
		case 2:
			h = h * Math.pow(-1.0, (double) (n));
			break;
		}
		return h;
	}

	public double y(int n, int m, int ka, Signal s, int wariant) {
		double result = 0;

		for (int k = 0; k <= m - 1; k++) {
			if ((n - k) < 0 || (n - k) > s.getSamples() - 1) {
				result += 0;
			} else {
				if (wariant == 1) {
					result += h(k, m, ka) * s.getContent()[n - k];
				}
				if (wariant == 2) {
					result += (0.5 - 0.5 * Math.cos(2.0 * Math.PI * k / m))
							* h(k, m, ka) * s.getContent()[n - k];
				}
				if (wariant == 3) {
					result += Math.sin(Math.PI * k / 2)
							* (0.5 - 0.5 * Math.cos(2.0 * Math.PI * k / m))
							* h(k, m, ka) * s.getContent()[n - k];
				}
				if (wariant == 4) {
					result += (0.5 - 0.5 * Math.cos(2.0 * Math.PI * k / m))
							* Math.pow(-1.0, k) * h(k, m, ka)
							* s.getContent()[n - k];
				}
			}
		}
		return result;
	}

	public Signal filtr(Signal s, int m, int k, int wariant) {
		double[] tmp = new double[s.getSamples()];
		for (int n = 0; n < s.getSamples() - 1; n++) {
			double sumaDyskretna = 0;
			for (int i = 0; i <= n; i++) {
				double czynnik1 = 0;
				double czynnik2 = 0;
				if (i >= m)
					czynnik1 = 0;
				else
					czynnik1 = h(i, m, k);
				if (n - i >= s.getSamples())
					czynnik2 = 0;
				else
					czynnik2 = s.getContent()[n - i];
				sumaDyskretna = sumaDyskretna + czynnik1 * czynnik2;
			}
			tmp[n] = sumaDyskretna;
		}
		return new Signal(s.getT0(), s.getF(), tmp.length, tmp);
	}

	public Signal DFT(Signal s) {
		double eN = s.getContent().length;
		double[][] tmp = new double[2][s.getContent().length];
		double s1 = 0.0;
		double s2 = 0.0;
		for (int m = 0; m < eN; m++) {
			s1 = 0.0;
			s2 = 0.0;
			for (int n = 0; n < eN; n++) {
				s1 += s.getContent()[n] * Math.cos(2.0 * Math.PI * n * m / eN);
				s2 += (-1.0) * s.getContent()[n]* Math.sin(2.0 * Math.PI * n * m / eN);
			}
			tmp[0][m] = s1;
			tmp[1][m] = s2;
		}

		Signal ret = new Signal(s.getT0(), s.getF(), tmp[0].length, tmp[0]);
		ret.setZespolona(true);
		ret.setcontentIm(tmp[1]);
		return ret;
	}

	public double[][] FFTt(double[] tab) {
		double eN = tab.length;
		
		double[][] tmp = new double[2][tab.length];
		/* deklaracja wektorow */
		double[] Wmn = new double[2];
		double[] Wm = new double[2];
		double[] parzyste = new double[2];
		double[] nieparzyste = new double[2];

		for (int m = 0; m <= eN-1; m++) {
			Wm[0] = Math.cos(2.0*Math.PI*m/eN); 	// Re W(m,N)
			Wm[1] = (-1.0)*Math.sin(2.0*Math.PI*m/eN); // Im W(m,N);
			// zerowanie uzywanych tablic
			parzyste[0]=0; parzyste[1]=0;
			nieparzyste[0]=0; nieparzyste[1]=0;
			for(int n=0; n<=eN/2-1;n++){
				Wmn[0] = Math.cos((2.0*Math.PI*n*m)/(eN/2));
				Wmn[1] = (-1.0)*Math.sin((2.0*Math.PI*n*m)/(eN/2));
				
				// suma parzystych
				parzyste[0]+=tab[2*n]*Wmn[0];
				parzyste[1]+=tab[2*n]*Wmn[1];
				
				nieparzyste[0]+=tab[2*n+1]*Wmn[0];
				nieparzyste[1]+=tab[2*n+1]*Wmn[1];
				
			}
			
			tmp[0][m]=parzyste[0]+(Wm[0]*nieparzyste[0]-Wm[1]*nieparzyste[1]);
			tmp[1][m]=parzyste[1]+(Wm[0]*nieparzyste[1]+Wm[1]*nieparzyste[0]);
			/*
			if(m !=0){
			tmp[0][(int) (eN-m)] = tmp[0][m];
			tmp[1][(int) (eN-m)] = -tmp[1][m];
			}
			*/
		}
		
		return tmp;
	}
	
	
	   public static Complex[] fft(Complex[] x) {
	        int N = x.length;
	        if (N == 1) return new Complex[] { x[0] };
	        if (N % 2 != 0) { throw new RuntimeException("Ilosc probek nie jest potega 2"); }
	        
	        Complex[] parzyste = new Complex[N/2];
	        for (int k = 0; k < N/2; k++) {
	            parzyste[k] = x[2*k];
	        }
	        Complex[] q = fft(parzyste);
	        
	        Complex[] nieparzyste  = parzyste;  
	        for (int k = 0; k < N/2; k++) {
	            nieparzyste[k] = x[2*k + 1];
	        }
	        Complex[] r = fft(nieparzyste);

	        Complex[] y = new Complex[N];
	        for (int k = 0; k < N/2; k++) {
	            double WkN = -2 * k * Math.PI / N;
	            Complex wk = new Complex(Math.cos(WkN), Math.sin(WkN));
	            y[k]       = q[k].plus(wk.times(r[k]));
	            y[k + N/2] = q[k].minus(wk.times(r[k]));
	        }
	        return y;
	    }
	
	
	public Signal DCT(Signal s) {
		double eN = s.getContent().length;
		double c = Math.sqrt(1.0 / eN);
		double[] content = new double[s.getContent().length];
		double suma = 0.0;
		for (int m = 0; m < eN; m++) {
			suma = 0.0;
			if (m == 0) {
				c = Math.sqrt(1.0 / eN);
			} else {
				c = Math.sqrt(2.0 / eN);
			}
			for (int n = 0; n < eN; n++) {
				suma += s.getContent()[n]
						* Math.cos((Math.PI * (2 * n + 1) * m) / (2 * eN));
				// System.out.println(suma);
			}

			content[m] = c * suma;
			System.out.println("SUMA : " + suma);
			System.out.println(" C: " + c);
			suma = 0.0;
		}
		Signal ret = new Signal(s.getT0(), s.getF(), content.length, content);
		return ret;
	}

	public Signal FCT(Signal s) {
		double[] y = new double[s.getContent().length];
		double eN = s.getContent().length;
		double[] tmp = new double[s.getContent().length];
		for (int n = 0; n < eN / 2; n++) {
			y[n] = s.getContent()[2 * n];
			y[(int) (eN - 1 - n)] = s.getContent()[2 * n + 1];
		}
		s.setContent(y);

		Signal d = DFT(s);
		//d.drawSignal();
		double[][] tmp2= new double[2][1];
		double[][] tmp3= new double[2][1];
		double[][] res= new double[2][1];
		for (int m = 0; m < eN; m++) {
			tmp2[0][0]=Math.cos(Math.PI*m/(2*eN));
			tmp2[1][0]=(-1.0)*Math.sin(Math.PI*m/(2*eN));
			tmp3[0][0]=d.getContent()[m];
			tmp3[1][0]=d.getcontentIm()[m];
			res=mult(tmp2,tmp3);
			tmp[m]=Math.sqrt(2/eN)*res[0][0];

		}
		Signal ret = new Signal(s.getT0(), s.getF(), tmp.length, tmp);
		return ret;

	}

	public double[][] mult(double[][] p, double[][] np) {
		double[][] tmp = new double[2][1];

		tmp[0][0] = p[0][0] * np[0][0] - p[1][0] * np[1][0];
		tmp[1][0] = p[1][0] * np[0][0] - p[0][0] * np[1][0];

		return tmp;

	}

	private double[][] matrixH(int n) {
		int rozmiar = (int) Math.pow(2, n);
		double[][] tmp = new double[rozmiar][rozmiar];
		double[][] H1 = new double[2][2];
		H1[0][0] = 1;
		H1[0][1] = 1;
		H1[1][0] = 1;
		H1[1][1] = -1;
		
		if (n != 1) {
			for (int i = 0; i < rozmiar; i++) {
				for (int j = 0; j < rozmiar; j++) {
					if (i < rozmiar/2) { // gorna polowa
						if (j < rozmiar/2)
							tmp[i][j] = matrixH(n - 1)[i][j];
						if (j >= rozmiar/2)
							tmp[i][j] = matrixH(n - 1)[i][j - rozmiar/2];
					}
					if (i >= rozmiar/2) { // dolna polowa
						if (j < rozmiar/2)
							tmp[i][j] = matrixH(n - 1)[i - rozmiar/2][j];
						if (j >= rozmiar/2)
							tmp[i][j] = - (matrixH(n - 1)[i - rozmiar/2][j - rozmiar/2]);
					}
				}
			}
		}
		if (n == 1)
			tmp = H1;
		return tmp;
	}

	// macierz razy wektor
	private double[] pomnozMW(double[][] m, double[] w) {
		int rozmiar = w.length;
		double[] result = new double[rozmiar];
		double suma = 0;

		for (int i = 0; i <= rozmiar-1; i++) {
			suma = 0.0;
			for (int j = 0; j < rozmiar; j++) {
				double temp = 0.0;
				temp = m[i][j] * w[j];
				temp= temp*0.1;
				temp = temp*10;
				suma += temp;
			}
			result[i] = suma;
		}
		return result;
	}

	// macierz razy stala
	private double[][] pomnozMC(double[][] m, double c) {
		int rozmiar = m.length;
		double[][] result = new double[rozmiar][rozmiar];
		for (int i = 0; i < rozmiar; i++) {
			for (int j = 0; j < rozmiar; j++) {
				result[i][j] = m[i][j] * c;
			}
		}
		return result;
	}

	public Signal WHT(Signal s) {
		int m =0;
		switch(s.getContent().length){
		case 2: m=1; break;
		case 4: m=2; break;
		case 8: m=3; break;
		case 16: m=4; break;
		case 32: m=5; break;
		case 64: m=6; break;
		case 128: m=7; break;
		case 256: m=8; break;
		case 512: m=9; break;
		case 1024: m=10; break;
		}
		double[] content = new double[s.getContent().length];
		double[][] hadamard = matrixH(m);
		// content = pomnozMW(pomnozMC(hadamard,
		// 1.0/Math.pow(2,eN/2)),s.getContent());
		content = pomnozMW(hadamard, s.getContent());
		Signal ret = new Signal(s.getT0(), s.getF(), content.length, content);
		return ret;
	}
	
	private double[] fwhta(double[] w, int n){
		int rozmiar = (int) Math.pow(2, n);
		double[] wynik = new double[rozmiar];
		double[] suma = new double[rozmiar/2];
		double[] roznica = new double[rozmiar/2];
		for (int i = 0; i <= (rozmiar / 2) - 1; i++) {
			suma[i] = w[i] + w[i + rozmiar / 2];
			roznica[i] = w[i] - w[i + rozmiar / 2];
		}
		
		if(n!=1){ 
			suma = fwhta(suma,n-1);
			roznica = fwhta(roznica,n-1);
			for (int i = 0; i < wynik.length; i++) {
				if (i < wynik.length/2)
					wynik[i] = suma[i];
				if (i >= wynik.length/2)
					wynik[i] = roznica[i - wynik.length/2];
			}
		}
		if(n==1){
			wynik[0]= w[0]+w[1];
			wynik[1]= w[0]-w[1];
		}
		return wynik;
		
	}
	
	public Signal FWHT(Signal s) {
		int m =0;
		
		switch(s.getContent().length){
		case 2: m=1; break;
		case 4: m=2; break;
		case 8: m=3; break;
		case 16: m=4; break;
		case 32: m=5; break;
		case 64: m=6; break;
		case 128: m=7; break;
		case 256: m=8; break;
		case 512: m=9; break;
		case 1024: m=10; break;
		}
		int rozmiar = s.getContent().length;
		double[] content = new double[rozmiar];
		double[] suma = new double[rozmiar / 2];
		double[] roznica = new double[rozmiar / 2];
		double[] gorna = new double[rozmiar / 2];
		double[] dolna = new double[rozmiar / 2];

		for (int i = 0; i <= (rozmiar / 2) - 1; i++) {
			suma[i] = s.getContent()[i] + s.getContent()[i + rozmiar / 2];
			roznica[i] = s.getContent()[i] - s.getContent()[i + rozmiar / 2];
		}
		
		//content = fwhta(content,m);

		gorna = fwhta(suma, m-1);
		dolna = fwhta(roznica, m-1);

		for (int i = 0; i < content.length; i++) {
			if (i < content.length / 2)
				content[i] = gorna[i];
			if (i >= content.length / 2)
				content[i] = dolna[i - content.length / 2];
		}
		
		Signal ret = new Signal(s.getT0(), s.getF(), content.length, content);
		return ret;
	}
}